<template>
  <el-upload
    class="avatar-uploader"
    action="#"
    :show-file-list="false"
    :on-success="handleAvatarSuccess"
    :before-upload="beforeAvatarUpload"
    :accept="imgType"
    :drag="drag"
    :disabled="disabled"
  >
    <S-msgWin :msg="callbackMessage" :duration="500" />
    <div v-if="imageUrl" @click.stop class="avatar-container relative group">
      <el-image
        class="avatar"
        :src="imageUrl"
        fit="cover"
        :preview-src-list="[imageUrl]"
      />
      <div
        v-if="!disabled"
        class="absolute inset-0 bg-black/30 opacity-0 group-hover:opacity-100 transition-opacity flex items-center justify-center"
      >
        <el-icon
          :size="30"
          color="white"
          class="edit-icon text-white mr-8 cursor-pointer"
          @click.stop="handleEditAvatar"
        >
          <Edit />
        </el-icon>

        <el-popconfirm title="确定删除吗?" @confirm="handleDeleteAvatar">
          <template #reference>
            <el-icon
              @click.stop
              :size="30"
              color="white"
              class="delete-icon text-white cursor-pointer"
            >
              <Delete />
            </el-icon>
          </template>
        </el-popconfirm>
      </div>
    </div>
    <el-icon v-else class="avatar-uploader-icon">
      <Plus />
    </el-icon>
  </el-upload>

  <el-dialog title="修改头像" v-model="editAvatarDialog" width="600">
    <el-row type="flex" justify="center" class="nowarp">
      <div class="cropper">
        <vue-cropper
          ref="cropper"
          v-bind="option"
          @realTime="realTime"
        ></vue-cropper>
      </div>
      <div class="previewBox">
        <div class="preview">
          <img :src="previews.url" />
        </div>

        <el-row type="flex" justify="center">
          <el-upload
            action="#"
            :show-file-list="false"
            :on-success="handleAvatarSuccess"
            :before-upload="beforeAvatarUpload"
          >
            <el-button size="small" type="primary"> 更换头像 </el-button>
          </el-upload>
        </el-row>
        <br />
        <el-row>
          <el-button
            :icon="ZoomIn"
            circle
            size="small"
            @click="changeScale(1)"
          ></el-button>
          <el-button
            :icon="ZoomOut"
            circle
            size="small"
            @click="changeScale(-1)"
          ></el-button>
          <el-button
            :icon="Download"
            circle
            size="small"
            @click="downloadPreView"
          ></el-button>
          <el-button
            :icon="RefreshLeft"
            circle
            size="small"
            @click="rotateLeft"
          ></el-button>
          <el-button
            :icon="RefreshRight"
            circle
            size="small"
            @click="rotateRight"
          ></el-button>
        </el-row>
      </div>
    </el-row>

    <template #footer>
      <div class="dialog-footer">
        <el-button @click="editAvatarDialog = false">取 消</el-button>
        <el-button type="primary" @click="editAvatarConfirm">确 定</el-button>
      </div>
    </template>
  </el-dialog>
</template>

<script setup lang="ts">
import "vue-cropper/dist/index.css";
import { VueCropper } from "vue-cropper";
import {
  ZoomIn,
  ZoomOut,
  Download,
  RefreshLeft,
  RefreshRight,
} from "@element-plus/icons-vue";
import { ref } from "vue";
import { Plus, Edit, Delete } from "@element-plus/icons-vue";
import type { UploadProps } from "element-plus";

const { imgType, drag, disabled, maxImgSize } = defineProps({
  imgType: {
    type: String,
    default: "image/*",
  },
  drag: {
    type: Boolean,
    default: false,
  },
  disabled: {
    type: Boolean,
    default: false,
  },
  maxImgSize: {
    type: Number,
    default: 2,
  },
});

const imageUrl = defineModel<string>();

const { uploadImage } = useImageUpload();
const editAvatarDialog = ref(false);
const imageName = ref("");

const previews = ref<any>({
  url: "",
  file: null,
});
// 响应式变量
const callbackMessage = useCallbackMessage();
const cropper = ref();

const option = ref({
  autoCrop: true, // 是否默认生成截图框
  autoCropHeight: "240px", // 默认生成截图框宽度(默认值：容器的 80%, 可选值：0 ~ max), 真正裁剪出来的图片的宽度为 autoCropHeight * 1.25
  autoCropWidth: "240px", // 默认生成截图框宽度(默认值：容器的 80%, 可选值：0 ~ max), 真正裁剪出来的图片的宽度为 autoWidth * 1.25
  canMove: true, // 上传图片是否可以移动
  canScale: true, // 图片是否允许滚轮缩放
  centerBox: true, // 截图框是否被限制在图片里面
  fixed: true, // 是否固定截图框的宽高比例
  fixedBox: true, // 是否固定截图框大小
  fixedNumber: [1, 1], // 截图框的宽高比例([ 宽度 , 高度 ])
  img: "", // 裁剪图片的地址(可选值：url 地址, base64, blob)
  info: true, // 是否显示裁剪框的宽高信息
  infoTrue: true, // infoTrue为 true 时裁剪框显示的是预览图片的宽高信息,infoTrue为 false 时裁剪框显示的是裁剪框的宽高信息
  mode: "contain", // 截图框可拖动时的方向(可选值：contain , cover, 100px, 100% auto)
  origin: false, // 上传的图片是否按照原始比例渲染
  outputSize: 1, // 裁剪生成图片的质量(可选值：0.1 ~ 1)
  outputType: "png", // 裁剪生成图片的格式(可选值：png, jpeg, webp)
  full: true,
});

const beforeAvatarUpload: UploadProps["beforeUpload"] = (rawFile) => {
  if (rawFile.size / 1024 / 1024 > maxImgSize) {
    callbackMessage.value = {
      show: true,
      valid: false,
      content: `图片大小不能超过${maxImgSize}MB!`,
    };
    return false;
  }
  return true;
};

const handleAvatarSuccess: UploadProps["onSuccess"] = (
  response,
  uploadFile
) => {
  // ! 为 TS 的非空断言
  option.value.img = URL.createObjectURL(uploadFile.raw!);
  editAvatarDialog.value = true;
  imageName.value = uploadFile.name;
};

// 实时预览
const realTime = () => {
  cropper.value.getCropBlob((blob: Blob) => {
    previews.value.url = window.URL.createObjectURL(blob);
    previews.value.file = blobToFile(blob, imageName.value);
  });
};

const editAvatarConfirm = async () => {
  editAvatarDialog.value = false;

  const res = await uploadImage(previews.value.file);
  if (Array.isArray(res?.data) && res.data.length) {
    imageUrl.value = res.data[0].url;
    imageName.value = res.data[0].filename;
    callbackMessage.value = {
      show: true,
      valid: true,
      content: `上传成功`,
    };
  } else {
    callbackMessage.value = {
      show: true,
      valid: false,
      content: `上传失败`,
    };
  }
};

const downloadPreView = () => {
  let aLink = document.createElement("a");
  aLink.download = "头像裁剪后的效果图.png";

  cropper.value.getCropBlob((blob: Blob) => {
    aLink.href = window.URL.createObjectURL(blob);
    aLink.click();
  });
};

const rotateLeft = () => {
  cropper.value.rotateLeft();
};

const rotateRight = () => {
  cropper.value.rotateRight();
};

const changeScale = (scaleSize: number) => {
  cropper.value.changeScale(scaleSize);
};

const handleDeleteAvatar = () => {
  if (!imageName.value) {
    imageName.value = imageUrl.value?.split("/").pop() || "";
  }
  $fetch(`/api/upload/delete`, {
    body: { filename: imageName.value },
    method: "POST",
  }).then((res) => {
    callbackMessage.value = {
      show: true,
      valid: true,
      content: `删除成功`,
    };
    imageUrl.value = "";
  });
};

const handleEditAvatar = () => {
  if (imageUrl.value) {
    imageName.value = imageUrl.value.split("/").pop() || "";
  }
  option.value.img = imageUrl.value || "";
  editAvatarDialog.value = true;
};
</script>

<style scoped>
.previewBox {
  text-align: center;
  margin-left: 60px;
}

.preview {
  width: 150px;
  height: 150px;
  margin: 0px auto 20px auto;
  border-radius: 50%;
  border: 1px solid #ccc;
  background-color: #ccc;
  overflow: hidden;
}

.cropper {
  width: 260px;
  height: 260px;
}

.avatar-uploader .avatar {
  width: 178px;
  height: 178px;
  display: block;
}

.avatar-container {
  position: relative;
}

.avatar:hover + .avatar-actions,
.avatar-actions:hover {
  display: flex;
}
</style>

<style>
.avatar-uploader .el-upload {
  border: 1px dashed var(--el-border-color);
  border-radius: 50% !important;
  cursor: pointer;
  position: relative;
  overflow: hidden;
  transition: var(--el-transition-duration-fast);
}

.avatar-uploader .el-upload:hover {
  border-color: var(--el-color-primary);
}

.el-icon.avatar-uploader-icon {
  font-size: 28px;
  color: #8c939d;
  width: 178px;
  height: 178px;
  text-align: center;
}
</style>
